package com.geezdata.cps.sdk3rd.kaola;

import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.alibaba.fastjson.JSONObject;
import com.geezdata.cps.base.enums.ErrorCode;
import com.geezdata.cps.base.response.BaseResponse;
import com.geezdata.cps.base.utils.HttpClientUtil;
import com.geezdata.cps.base.utils.MapUtil;
import com.geezdata.cps.base.utils.Md5Util;
import com.geezdata.cps.sdk3rd.kaola.request.KaolaRequest;
import com.geezdata.cps.sdk3rd.kaola.response.KaolaResult;

public class KaolaDefaultClient implements KaolaClient {
    private String serverUrl = null;
    private String accessToken = null;
    private String appKey = null;
    private String appSecret = null;
    
    
    public KaolaDefaultClient(String serverUrl, String accessToken, String appKey, String appSecret) {
        super();
        this.serverUrl = serverUrl;
        this.accessToken = accessToken;
        this.appKey = appKey;
        this.appSecret = appSecret;
    }

    public <T extends KaolaResult> BaseResponse<T> execute(KaolaRequest<T> request) {
        String rspStr = null;
        try {
            rspStr = HttpClientUtil.post(serverUrl, prepareHttpParams(request));
        } catch (Exception e) {
        	return BaseResponse.error(ErrorCode.HTTP_EXCEPTION.getCode(), e.getMessage());
        }
        
        JSONObject jsonObject = JSONObject.parseObject(rspStr);
        if (jsonObject.containsKey("error_response")){
        	return BaseResponse.error(ErrorCode.HTTP_EXCEPTION.getCode(), 
        			jsonObject.getJSONObject("error_response").toString());
        } 
        
		T kaolaResult = (T) JSONObject.toJavaObject(jsonObject, request.getResponseClass());
		return (BaseResponse<T>) BaseResponse.success(kaolaResult);
    }
    
    /**
     * 获得请求接口的所有参数键值
     * @param kaolaRequest
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private Map<String, Object> prepareHttpParams(KaolaRequest kaolaRequest) {

        Map<String, Object> httpParams = new HashMap<String, Object>();

        // 获取系统级别输入参数
        httpParams.put("method", kaolaRequest.getMethodName());
        httpParams.put("access_token", accessToken);
        httpParams.put("app_key", appKey);
        kaolaRequest.setTimestamp(new Date()); 
        httpParams.put("timestamp", kaolaRequest.getTimestamp());
        httpParams.put("v", kaolaRequest.getV());

        // 获取应用级别参数
		Map<String, Object> appParams = kaolaRequest.getAppParams();
        Iterator<String> it = appParams.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            Object value = appParams.get(key);
            httpParams.put(key, value);
        }
        
        //去除空参数
        MapUtil.removeNull(httpParams);
        
        // 获得sign
        httpParams.put("sign", createSign(httpParams));

        return httpParams;
    }
    
    /**
     * <p>生成签名并返回
     * 
     * <p>生成步骤：
     * 
     * <p>1.所有请求参数按照字母先后顺序排列<br>
     * 注：<br>
     * 必须参数和有值的可选参数才参与签名生成<br>
     * 
     * <p>2.把所有参数名和参数值进行拼装<br>
     * 注：<br>
     * 去掉等号<br>
     * 除日期和时间中间的空格之外，不允许在其它位置出现空格
     * 
     * <p>3.把appSecret夹在字符串的两端<br>
     * 注：<br>
     * appSecret值参与签名，不用加入键<br>
     * 拼接后的字符串不允许有跨行<br>
     * 时间戳timestamp 最好就填写当前时间的前几分钟（必须在6分钟之内）
     * 
     * <p>4.使用MD5进行加密，再转化成大写<br>
     * 注：<br>
     * byte2hex(md5(appSecret+XXXX+appSecret))<br>
     * hex为自定义方法，JAVA中MD5是对字节数组加密，加密结果是16字节<br>
     * 我们需要的是32位的大写字符串，图片参数不用加入签名中
     * 
     * @return sign
     */
    private String createSign(Map<String, Object> httpParams){
        
        // 请求参数排序
        Object[] keySet = httpParams.keySet().toArray();
        Arrays.sort(keySet);

        // 请求参数拼装
        StringBuffer sb = new StringBuffer();
        sb.append(appSecret); // 考拉要求字符串前后加app_secret
        for (Object key : keySet) {
            Object value = httpParams.get(key);
            sb.append(key.toString() + value.toString());
        }
        sb.append(appSecret); // 考拉要求字符串前后加app_secret
        String sign = Md5Util.md5(sb.toString()).toUpperCase().trim();

        return sign;
    }
}
